home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / ArchiveUtils / TARInspector1.0 / Source / TARInspector.m < prev   
Text File  |  1995-06-12  |  12KB  |  297 lines

  1. /***************************************************************************
  2. TARInspector 1.0 (a .tar file content inspector module for the NeXTSTEP Workspace Manager)
  3. Copyright (C) 1993    Glenn Brown
  4.  
  5. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.
  6.  
  7. This program is distributed WITHOUT ANY WARRANTY; without even the implied warrenty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  8.  
  9. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  10. ***************************************************************************/
  11.  
  12. /*Note:  Don't be intimidated by the size of this file.  It is so big because is has copious amounts of comments
  13.     You should read GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf before trying to understand this code.  It's really simple, though:  The workspace manager simply calls the ok: method whenever the user presses the ok ('Unarchive') button and calls revert:  whenever the revert ('List Contents') button is pressed -OR- the workspace manager wants the inspector to update its display.  The new method is called the first time TARInspector is used during a login session.*/
  14.  
  15. #import <mach/cthreads.h>
  16. #import <stdio.h>
  17. #import <mach/cthreads.h>
  18. #import "TARInspector.h"
  19.  
  20. @implementation TARInspector
  21.  
  22. static id tarInspector = nil;
  23. //    I hate globals, but its in the 
  24. //    GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf example and
  25. //    I say:  if it ain't broke...
  26.  
  27. /***********
  28. * new:  This routine is based on the new routine found in 
  29. * the online developer documentation modified as noted.  See 
  30. * GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf
  31. ************/
  32. + new
  33. {    if (tarInspector == nil) {
  34.         char path[MAXPATHLEN+1];
  35.         NXBundle *bundle = [NXBundle bundleForClass:self];
  36.       
  37.         self = tarInspector = [super new];
  38.         if ([bundle getPath:path 
  39.                 forResource:"TARInspector"
  40.                 ofType:"nib"]) {
  41.             [NXApp loadNibFile:path owner:tarInspector];
  42.             
  43.             //vvvvvvvvvvvvvvvvvvvvvvvvGlenn's modifications
  44.             instructions = NXOpenMemory(NULL,0,NX_READWRITE);
  45.             [textOutput writeRichText:instructions];
  46.             //    the above two lines open a stream in memory and write the
  47.             //    rtf format TARInspector instructions just loaded from the
  48.             //    .nib file into it so the can be reloaded into the 
  49.             //    scroller later.
  50.             //^^^^^^^^^^^^^^^^^^^^^^^^
  51.             
  52.         } else {
  53.             fprintf (stderr, "Couldn't load TARInspector.nib\n");
  54.             tarInspector = nil;
  55.         }
  56.     }
  57.     return tarInspector;
  58. }
  59.  
  60. /************
  61. * asynchronouslyPerformFileOperation: This is a function rather than
  62. * a method so that it can be executed asynchronously.  See where it is
  63. * called for details.  Also see
  64. * GeneralReference/02_ApplicationKit/Protocols/NXWorkspaceRequestProtocol.rtf
  65. *************/
  66. int    asynchronouslyPerformFileOperation(char *sels){
  67.     int err;
  68.     
  69.     err = [[Application workspace] 
  70.     performFileOperation: WSM_DECOMPRESS_OPERATION
  71.     source: ""
  72.     destination:""
  73.     files: sels
  74.     options:""];
  75.     //    Decompresses the selections using the workspace's decompress
  76.     //    feature.  The workspace does nice things during decompression like
  77.     //    putting a message in the file viewer window and showing data about
  78.     //    the decompression operation in the processes window.
  79.     //        Unfortunately, the 'Unarchive' feature of the workspace manager is
  80.     //    either not documented or does not exist as a 'File Operation'.
  81.     //    Probably, the inspector that comes with 3.0 does not use the
  82.     //    performFileOperation:...: method to do unarchiving but, rather, does
  83.     //    everything (messages, e.g.) directly.  I could not do this, however,
  84.     //    since the internals of the Workspace manager are not documented.
  85.                 
  86.     free(sels);
  87.     return err;    
  88. }
  89.  
  90. /*************
  91. * updateButtons:  updates the buttons as recorded in the objects
  92. * instance variables okEnabled and revertEnabled.
  93. **************/
  94. - updateButtons{
  95.     [[[self okButton] setEnabled:okEnabled] setTitle:"Unarchive"];
  96.     [[[self revertButton] setEnabled:revertEnabled] setTitle:"ListContents"];
  97.     return self;
  98. }
  99.  
  100. /************
  101. * ok: This method is called by the workspace when the OK ("Unarchive") button
  102. * is pressed. 
  103. * See GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf 
  104. *************/
  105. - ok:sender{
  106.     unsigned numSels;
  107.     char *sels;
  108.     
  109.     okEnabled = NO;
  110.     [self updateButtons]; 
  111.     //    deactivate the "Unarchive" button so the user will not unarchive
  112.     //    the file twice.
  113.     numSels = [self selectionCount]; 
  114.     //    find out how many files are selected and ...
  115.     sels = calloc(numSels*(MAXPATHLEN+1),sizeof(char));
  116.     //    allocate enough memory to hold the numSels selections plus the
  117.     //  seperators between them plus the null termination.
  118.     if(sels){
  119.     //    if memory allocation was successful
  120.         [self selectionPathsInto:sels separator:'\t'];
  121.         //    read the names of the selected files into the allocated space
  122.         //  using the seperator that the WorkspaceRequestProtocol asks for.
  123.         cthread_detach(cthread_fork(
  124.         (cthread_fn_t)asynchronouslyPerformFileOperation,
  125.         (any_t)sels));
  126.         //    Tells the workspace manager to decompress the file.
  127.         //    This call requires a little explaining!
  128.         //        Because this inspector module executes as part of the
  129.         //    workspace manager's main thread of execution, and because the
  130.         //    performFileOperation:..: method blocks until workspace manager
  131.         //    responds to the workspace request, the workspace manager will
  132.         //  hang unless a seperate thread makes the workspaceManagerRequest.
  133.         //        At least that's the only explaination I could come up with
  134.         //    for why the workspace manager would hand during the request
  135.         //    before I put the request in another thread!
  136.     }else{
  137.         //    Insert error messager here if you want to.  It will be called
  138.         //    if memory for the selection could not be allocated.
  139.         return nil;
  140.     }
  141.     [super ok:sender];    
  142.     return self;
  143.     //    Above 2 lines as spec'd in 
  144.     //    GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf
  145. }
  146.  
  147.  
  148. /************
  149. * listContentsOfTarFile:  This method just calls 
  150. * 'tar -tf /selection/' and pipes the result into the Text object in
  151. * the ScrollView.  Assumes there is only one selection.
  152. *************/
  153. - listContentsOfTarFile:sender{
  154.     char command[13+MAXPATHLEN+1]; 
  155.     //    space for the command line to send to 'sh'
  156.     //  13 for "/bin/tar -tf ", MAXPATHLEN for file path, 1 for null at end
  157.     FILE *result;    //file (stream) holding text result of 'tar -tf command'
  158.     NXStream *nxf;    //will point to same stream as result.
  159.     strcpy(command,"/bin/tar -tf ");    
  160.     //    copy the command to the command line
  161.     [self selectionPathsInto:&command[13] separator:'\0'];
  162.     //    puts the pathname of the selected file at the end of the command line.
  163.     [[textOutput setMonoFont:YES] setText:""];
  164.     //    erase text in inspector and set mode to unformatted text
  165.     [[textOutput selectAll:self] alignSelLeft:self];
  166.     [textOutput setSelProp:NX_INDENT to:30];
  167.     [[textOutput setSelFontSize:10.0] setSelFontStyle:0];
  168.     //    3 lines above set text display attributes.
  169.     [[textOutput selectNull] sizeToFit];    //    update display
  170.     result = popen(command,"r");        
  171.     //execute the command and store results in a stream called result.
  172.     if(result != NULL){                    //if tar -tf success then
  173.         nxf=NXOpenFile(fileno(result),NX_READONLY);
  174.         //    converts (FILE *)result to a (NXStream *)
  175.         if(nxf!=NULL){                    //if nothing weird happened
  176.             [textOutput readText:nxf];    //put result in text field
  177.  //            [textOutput sizeToFit];        //update display
  178.             NXClose(nxf);                //close the stream
  179.         }else{
  180.             //If there is an error here, I want to know about it!
  181.             //I've never seen this message, however.
  182.             [textOutput setText:"Error (1) listing contents."];
  183.         }
  184.         pclose(result);                    
  185.         //    closes the file (stream) that the result were read into.
  186.         //    I'm not sure that I need to close it, since NXClose was called
  187.         //    above, but in the case that nxf == null, NXClose will not be
  188.         //    called and the file should be closed.  Also, the pclose man
  189.         //    page sez that pclose can tell is its arg is not something that
  190.         //    needs to be closed with pclose.
  191.         
  192.     }else [textOutput setText:"Error (2) listing contents."];
  193.     //I would be amazed if this error were reported.
  194.     [textOutput sizeToFit];    //    make all text visible.
  195.     [super revert:sender];    
  196.     //    as spec'd in GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf
  197.     return self;
  198. }
  199.  
  200. /************
  201. ** isNewSelection
  202. ** Tests if a new selection has been made.  If so, it records the new
  203. ** selection as LastSelection along with lastSelection count.
  204. ** This method is only necessary because NeXT doesn't tell us this info
  205. ** directly... [Lame!]
  206. **************/
  207. - (int) isNewSelection{
  208.     char *currentSelection;
  209.     int currentSelectionCount;
  210.     currentSelectionCount = [self selectionCount]; //read current # selected
  211.     currentSelection = calloc(                    //alloc space for the
  212.         currentSelectionCount*(MAXPATHLEN+1),    //tab delimited selection
  213.         sizeof(char));                            //list
  214.     if(currentSelection){                            //if alloc succeeded
  215.         [self selectionPathsInto:currentSelection separator: '\t'];
  216.                                                 //read path into alloc'ed space
  217.         if(lastSelection==NULL){    
  218.             //if this is first time to read a selection, then the current
  219.             //selection, whatever it is, is new.
  220.             lastSelection=currentSelection;        //record the current selection
  221.             lastSelectionCount=currentSelectionCount;
  222.             return YES;                            //and report that it was new
  223.         }else if(strcmp(currentSelection,lastSelection)){
  224.                 //if current and prev sels are different...
  225.                 //(note: strcmp will _not_ be called if cS or lS is null
  226.             lastSelectionCount = currentSelectionCount; //then update count
  227.             free(lastSelection);                 //free the old sel info
  228.             lastSelection = currentSelection;    //update selection
  229.             return YES;                            //report that sel is new
  230.         }else{                                //if current selection is not new
  231.             free(currentSelection);            //free it.
  232.         }
  233.     }/*else{Here would be a good place for an error message}*/
  234.     return NO;    //returned if current selection could not be alloc'd
  235.                 //or current selection is not new
  236. }
  237.  
  238. /*************
  239. ** updateButtonsForNewSelection:  If there is a new selection, enable
  240. ** the appropriate buttons
  241. **************/
  242. - updateButtonsForNewSelection{
  243.     okEnabled = YES;                            //enable 'Unarchive' button
  244.     revertEnabled = ([self selectionCount]==1);
  245.     //    enable 'List Conents' button iff one file is selected
  246.     [self updateButtons];                        //update the buttons.
  247.     return self;
  248. }
  249.  
  250. /*************
  251. ** displayInstructions: puts the instructions (which were read earlier
  252. ** from the Text object just after they were loaded from the .nib file)
  253. ** back in the text display
  254. **************/
  255. - displayInstructions{
  256.     [textOutput setText:""];        //force Text object to scroll to top
  257.     NXSeek(instructions, 0L, NX_FROMSTART);
  258.     //    rewind the stream holding the instructions to the start.
  259.     [[textOutput setMonoFont:NO] readRichText:instructions];
  260.     //  enable RTF mode and load the RTF instructions into the display
  261.     return self;
  262. }
  263.  
  264. /*************
  265. ** revert:  This is the heart of the inspector.  This method is called if
  266. ** the workspace manager wants the inspector to be updated or the revert
  267. ** button is pressed.
  268. ** See GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf.
  269. **************/
  270. - revert:sender{
  271.     if([self revertButton] != sender){
  272.         //    This code is executed iff the revert button was _not_ the cause.
  273.         //    of the revert message being sent... Meaning the workspace
  274.         //    manager wants a display update.
  275.         if([self isNewSelection]){
  276.         //    if a new selection has been made
  277.             [self updateButtonsForNewSelection];
  278.             //    update buttons as appropriate.
  279.             [self displayInstructions];
  280.             //    erase old content listing by replacing with instructions
  281.         } else [self updateButtons];
  282.     }else if([self selectionCount]==1){    
  283.         //    This branch is taken only if the user actually pressed
  284.         //    the button and only one file is selected.
  285.         revertEnabled = NO;
  286.         [self updateButtons];
  287.         //    above two lines disable the 'List Contents' button so the
  288.         //    user cannot waste cycles listing the contents more than once!
  289.         [self listContentsOfTarFile:sender];  //listContentsOf the selected TarFile
  290.     }
  291.     [super revert:sender];        
  292.     //    as specified in GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf
  293.     return self;
  294. }
  295.  
  296. @end
  297.